﻿
#include "FileUploader.h"

#include <QFile>
#include <QDataStream>

#include <Data/User.h>
#include "Network/Manager.h"
#include "Network/Connection.h"

using namespace AliOSS;

namespace TC
{
    namespace Request
    {
        void FileUploader::upload(const QString &filepath)
        {
            auto fixedPath = QUrl(filepath).toLocalFile();
            QFile f(fixedPath);
            if (!f.open(QIODevice::ReadOnly))
            {
                setError(QObject::tr("Failed to open file : ").arg(filepath)); // "打开文件失败！"
                return;
            }
			QString suffix;
			thread_local QRegExp suffixRegex = QRegExp(".*\\.(.*)$");
			if (suffixRegex.indexIn(filepath) != -1)
			{
				suffix = suffixRegex.cap(suffixRegex.captureCount());
			}

			QByteArray data = f.readAll();
			uploadData(data, suffix);
        }

		void FileUploader::uploadString(const QString &strs, const QString &suffix)
		{
			uploadData(strs.toUtf8(), suffix);
		}

		void FileUploader::uploadData(const QByteArray &data, const QString &suffix)
		{
			initGlobal();
			setRemoteUrl(QString());
			AliOSSUploadRequest *uploader = adapter->prepareUpload(data);
			Connection *socket = Network::Manager::instance()->get("upload/prepare", {
				{ "signature", Data::User::instance()->signature() },
				{ "suffix", suffix },
// 				{ "datetime", uploader->datetimeString() },
				{ "md5inbase64", uploader->fileMd5inBase64() }
			});

			// normal signal handling
			QObject::connect(socket, &QSslSocket::readyRead, [this, uploader, socket]()
			{
				FileUploader::onSocketReadyRead(uploader, socket);
			});

			// error-handling
			QObject::connect(socket, static_cast<void(QSslSocket::*)(const QList<QSslError> &)>(&QSslSocket::sslErrors), this, &FileUploader::autoSetSslError);
			QObject::connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), this, &FileUploader::autoSetSocketError);
			QObject::connect(socket, &QSslSocket::disconnected, this, &FileUploader::onSocketDisconnected);

			// auto-delete
			QObject::connect(socket, static_cast<void(Connection::*)(const QString &)>(&Connection::error), socket, &Connection::deleteLater);
			QObject::connect(socket, static_cast<void(Connection::*)(const QString &)>(&Connection::error), uploader, &AliOSSUploadRequest::deleteLater);
			QObject::connect(socket, &Connection::disconnected, socket, &Connection::deleteLater);

			setCommunicationing();
		}

		void FileUploader::onUploadProgress(qint64 bytesSent, qint64 bytesTotal)
        {
            this->setProgress((double)bytesSent / bytesTotal);
        }

        void FileUploader::onSocketReadyRead(AliOSSUploadRequest *uploader, Connection *socket)
        {
            auto data = socket->readAll();
            socket->disconnect();

            QDataStream stream(&data, QIODevice::ReadOnly);
            bool success;
            stream >> success;
            if (success)
			{
				QString datetimestr;
				QString path;
				QString signature;
				QString contentType;
				QMap<QString, QByteArray> ossHeaders;
                stream >> datetimestr >> path >> signature >>  contentType >> ossHeaders;
                uploader->setNeededInformation(
					std::move(datetimestr), 
					std::move(path),
					std::move(signature), 
					std::move(contentType),
					std::move(ossHeaders));

                // signals
                QObject::connect(uploader, &AliOSSUploadRequest::uploadProgress, this, &FileUploader::onUploadProgress);
                QObject::connect(uploader, &AliOSSUploadRequest::uploadFinished, this, &FileUploader::setOk);

				// error handling
                QObject::connect(uploader, &AliOSSUploadRequest::uploadFailed, this, &FileUploader::setFailed);
                QObject::connect(uploader, &AliOSSUploadRequest::uploadError, this, &FileUploader::autoSetHttpError);

                // auto delete
                QObject::connect(uploader, &AliOSSUploadRequest::uploadFinished, uploader, &AliOSSUploadRequest::deleteLater);
                QObject::connect(uploader, &AliOSSUploadRequest::uploadFailed, uploader, &AliOSSUploadRequest::deleteLater);
                QObject::connect(uploader, &AliOSSUploadRequest::uploadError, uploader, &AliOSSUploadRequest::deleteLater);

                uploader->doUpload();
                this->setRemoteUrl(uploader->remoteUrl());
            }
            else
            {
                this->setFailed();
                delete uploader;
            }
        }

        void FileUploader::onSocketDisconnected()
        {
            if (status() == Status::Communicating)
                AbstractProcedure::setError("Remote closed the connection with no reason.");
        }

		void FileUploader::initGlobal()
		{
			if (nullptr != adapter)
				return;
			adapter = new AliOSSAdapter("tcres", "sSot2oqZBG1xTcJs");
        }

		AliOSS::AliOSSAdapter * FileUploader::adapter = nullptr;

	}
}
